package cn.omisheep.commons.util;

import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 用于生成定时任务
 *
 * @author zhouxinchen[1269670415@qq.com]
 * @since 1.0.0
 */
public class TaskBuilder {

    private TaskBuilder() {
        throw new UnsupportedOperationException();
    }

    /**
     * 缓存任务计数
     */
    private static final AtomicInteger taskNumber = new AtomicInteger(1);

    /**
     * 定时器
     */
    private static ScheduledExecutorService timer;

    static {
        create();
    }

    /**
     * 请自行包裹异常，否则抛出异常将暂停
     *
     * @param task          任务
     * @param periodTimeVal 周期时间
     * @return ScheduledFuture
     */
    public static ScheduledFuture<?> schedule(Runnable task,
                                              String periodTimeVal) {
        return schedule(task, TimeUtils.parseTimeValue(periodTimeVal), TimeUnit.MILLISECONDS);
    }

    /**
     * 请自行包裹异常，否则抛出异常将暂停
     *
     * @param task     任务
     * @param period   周期时间
     * @param timeUnit 周期时间单位
     * @return ScheduledFuture
     */
    public static ScheduledFuture<?> schedule(Runnable task,
                                              long period,
                                              TimeUnit timeUnit) {
        if (period == 0) return null;
        return timer.scheduleAtFixedRate(task, 0, period, timeUnit);
    }

    /**
     * 延迟多久之后启动周期任务
     * 请自行包裹异常，否则抛出异常将暂停
     *
     * @param task          任务
     * @param delayTimeVal  延迟时间
     * @param periodTimeVal 周期时间
     * @return ScheduledFuture
     */
    public static ScheduledFuture<?> scheduleDelay(Runnable task,
                                                   String delayTimeVal,
                                                   String periodTimeVal) {
        return scheduleDelay(task, TimeUtils.parseTimeValue(delayTimeVal), TimeUtils.parseTimeValue(periodTimeVal),
                             TimeUnit.MILLISECONDS);
    }

    /**
     * 延迟多久之后启动周期任务
     * 请自行包裹异常，否则抛出异常将暂停
     *
     * @param task     任务
     * @param delay    延迟时间
     * @param period   周期时间
     * @param timeUnit 周期时间单位
     * @return ScheduledFuture
     */
    public static ScheduledFuture<?> scheduleDelay(Runnable task,
                                                   long delay,
                                                   long period,
                                                   TimeUnit timeUnit) {
        if (period == 0) return scheduleOnceDelay(task, delay, timeUnit);
        return timer.scheduleAtFixedRate(task, delay, period, timeUnit);
    }

    public static ScheduledFuture<?> scheduleOnce(Runnable task) {
        return timer.schedule(task, 0, TimeUnit.MICROSECONDS);
    }

    public static ScheduledFuture<?> scheduleOnceDelay(Runnable task,
                                                       String delayTimeVal) {
        return scheduleOnceDelay(task, TimeUtils.parseTimeValue(delayTimeVal), TimeUnit.MILLISECONDS);
    }

    public static ScheduledFuture<?> scheduleOnceDelay(Runnable task,
                                                       long delay,
                                                       TimeUnit timeUnit) {
        if (delay == 0) return scheduleOnce(task);
        return timer.schedule(task, delay, timeUnit);
    }

    /**
     * 创建定时器
     */
    public static void create() {
        if (null != timer) {
            shutdownNow();
        }
        timer = new ScheduledThreadPoolExecutor(1,
                                                r -> newThread(r, StringUtils.format("Timer-{}",
                                                                                     taskNumber.getAndIncrement()))
        );
    }

    /**
     * 销毁全局定时器
     */
    public static void shutdown() {
        if (null != timer) {
            timer.shutdown();
        }
    }

    /**
     * 销毁全局定时器
     *
     * @return 销毁时未被执行的任务列表
     */
    public static List<Runnable> shutdownNow() {
        if (null != timer) {
            return timer.shutdownNow();
        }
        return null;
    }

    private static Thread newThread(Runnable runnable,
                                    String name) {
        final Thread t = new Thread(null, runnable, name);
        t.setDaemon(false);
        return t;
    }

}
